package com.huiquan.analysis.service;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 计算两个文档的相似度.
 * 
 * @author renhao.cao
 * 		   Create 2017年4月1日.
 */
public class SimilarityAlgo {
	
	/**
	 * 获取两篇文档的相似度.
	 * 
	 * @param doc1 文档1
	 * @param doc2 文档2
	 * @return double
	 */
	public static double getSimilarity (String doc1, String doc2) {
		String[] words1 = doc1.split(",");
		String[] words2 = doc2.split(",");
		
		// 1.统计词集合及两个文档的词频集合
		List<String> words = new ArrayList<String>();
		Map<String, Integer> wordCntMap1 = new HashMap<String, Integer>();
		Map<String, Integer> wordCntMap2 = new HashMap<String, Integer>();
		getAllWords(words, wordCntMap1, wordCntMap2, words1, words2);
		
		// 3.生成两篇文档各自的计算向量
		double[] vector1 = new double[words.size()];
		double[] vector2 = new double[words.size()];
		for(int i = 0; i < words.size(); i++) {
			vector1[i] = wordCntMap1.containsKey(words.get(i)) ? wordCntMap1.get(words.get(i)) : 0;
			vector2[i] = wordCntMap2.containsKey(words.get(i)) ? wordCntMap2.get(words.get(i)) : 0;
		}
		
		// 4.计算两个向量的余弦相似度
		double cosSim = calCosSim(vector1, vector2);
		
		return format(cosSim, 2);
	}
	
	private static void getAllWords(List<String> allWordsList, Map<String, Integer> wordCntMap1, 
			Map<String, Integer> wordCntMap2, String[] words1, String[] words2) {
		Set<String> set = new HashSet<String>();
		for(String word : words1) {
			if(set.add(word))
				allWordsList.add(word);
			if(wordCntMap1.containsKey(word))
				wordCntMap1.put(word, wordCntMap1.get(word)+1);
			else
				wordCntMap1.put(word, 1);
		}
		for(String word : words2) {
			if(set.add(word))
				allWordsList.add(word);
			if(wordCntMap2.containsKey(word))
				wordCntMap2.put(word, wordCntMap2.get(word)+1);
			else
				wordCntMap2.put(word, 1);
		}
	}
	
	/**
	 * 求两个向量的余弦相似度.
	 *
	 * @param vector1s
	 * @param vector2s
	 * @return double
	 */
	private static double calCosSim(double[] vector1s, double[] vector2s) {
		double vec1sSquare = 0d;
		double vec2sSquare = 0d;
		double vec1sMultiplyVec2s = 0d;
		
		int length = vector1s.length;
		for(int i = 0; i < length; i++) {
			vec1sSquare += (vector1s[i] * vector1s[i]);
			vec2sSquare += (vector2s[i] * vector2s[i]);
			vec1sMultiplyVec2s += (vector1s[i] * vector2s[i]);
		}
		
		double ret = vec1sMultiplyVec2s/
				((Math.sqrt(vec1sSquare)) * (Math.sqrt(vec2sSquare)));
		return ret;
	}
	
	/**
     * DecimalFormat转换最简便
     * 
	 * @param value 需要转换的值
	 * @param length 需要的小数点后长度
	 * @return double
     */
    private static double format(double value, int length) {
    	StringBuffer sb = new StringBuffer();
    	sb.append("#.");
    	for(int i = 0; i < length; i++) {
    		sb.append("0");
    	}
    	DecimalFormat df = new DecimalFormat(sb.toString());
		double ret = Double.parseDouble(df.format(value));
		
		return ret;
    }

}
